  /*
  TP3 Algorithmes Parallèles de Tri:
  Tri àBulles:
  p processeurs utilisés pour une liste àtrier de taille n.
  */

#include "arch.h"
#include <stdlib.h>

//user program
Global_id gid1;
Global_id gid2;
Coll_id cid1,cid2;

//la taille de la talbe
#define N 80
//nb de processeur
#define P 8
//valeur max du nombre aléatoire tiré
#define MYRAND_MAX 500

void init_gid(int, char **){
   gid1.set();
   gid2.set();
   cid1.set();
   cid2.set();
   fprintf(Sdf::trace, "\nnode%d: gid1.tag=%d, gid2.tag=%d, cid1.tag=%d, cid2.tag=%d\n",
           Sdf::PROC_ID, gid1.get(), gid2.get(), cid1.get(),cid2.get());
   fflush(Sdf::trace);
}

//Compteur qui va gerer les appels de handler
class SR_count{
   public:
      int cnt;
      SR_count(){
         cnt = 0;
   	}
      void reset(){
         cnt = 0;
      }
      void more_SR(int i){
         cnt = cnt + i;
      }
};

//gestion des handler
void upon_end_of_SR_r(ChanAs *chan){           

   if(!(((SR_count *)(chan->get_hndlr_data_ptr(Sdf::RCV)))->cnt -= 1))
      Sdf::ROOT_THREAD->schedule();

}

void upon_end_of_SR_s(ChanAs *chan){ 
   if(!(((SR_count *)(chan->get_hndlr_data_ptr(Sdf::SND)))->cnt -= 1))
      Sdf::ROOT_THREAD->schedule();

}


class Tableau{
   public:
        int table[N/P];
};

ChanA<Tableau> *proc_to_left;
ChanA<Tableau> *proc_to_right;

int compare (const void * a, const void * b)
{
  return ( *(int*)a - *(int*)b );
}

class Root_proc: public Thread{

   public:

Tableau table_dist,table_locale;

int table_fusion[2*N/P];
int proc_id;

        Root_proc()
                : Thread((Tmf0_ptr)&Root_proc::body)
								{
										proc_id= Sdf::PROC_ID;
								}

        void body(){

				/* Génération des nombres pseudo-aléatoirement*/
								srand (time (NULL));

								for(int j=0; j<N/P; j++)
								{;
										table_locale.table[j] = rand() %MYRAND_MAX;
								}

/***********************/
										fprintf(Sdf::trace, "node %d , Remplissage de la table locale : \t", proc_id);
										fflush(Sdf::trace);

								for(int j=0; j<N/P; j++)
									{
										fprintf(Sdf::trace, "%d\t", table_locale.table[j]);
										fflush(Sdf::trace);
									}
										fprintf(Sdf::trace, "\n");
					 					fflush(Sdf::trace);
/*************************/

										qsort (table_locale.table, N/P , sizeof(int), compare);

/***********************/
										fprintf(Sdf::trace, "node %d , Triage de la table locale : \t", proc_id);
										fflush(Sdf::trace);

								for(int j=0; j<N/P; j++)
									{
										fprintf(Sdf::trace, "%d\t", table_locale.table[j]);
										fflush(Sdf::trace);
									}
										fprintf(Sdf::trace, "\n");
					 					fflush(Sdf::trace);
/*************************/

                //proc 0 -> NOPID (-1)
                //proc 1 -> 0
                //proc 2 -> 1
                int left_id = ((proc_id > 0)? proc_id-1 : Sdf::NOPID);
                proc_to_left = new ChanA<Tableau>(gid1, left_id);
                   //synchronous

                // proc 0 -> 1
                // proc 1 -> 2
                // proc 2 -> NOPID (-1)
								int right_id = ((proc_id < Sdf::PROC_NB-1)? proc_id+1 : Sdf::NOPID);
                proc_to_right = new ChanA<Tableau>(gid2, right_id);
	
		        	SR_count c;

            	proc_to_left->set(Sdf::SND, upon_end_of_SR_s, &c);
            	proc_to_left->set(Sdf::RCV, upon_end_of_SR_r, &c);

            	proc_to_right->set(Sdf::SND, upon_end_of_SR_s, &c);
            	proc_to_right->set(Sdf::RCV, upon_end_of_SR_r, &c);		

                   //synchronous
               Sdf::barrier(cid1);
                //synchronizes chan construction(no longer
                //necessary starting from version 5)

							for(int k = 0; k < P; k++){
/*processeur pair	*/
								if(!(proc_id%2)){

/*PHASE 1*/				if(proc_id < P -1){
									c.more_SR(2);

									//envoyer table àproc. proc_id+1;
									proc_to_right->send(Sdf::STD,table_locale);
									//recevoirdans t la donnée table envoyée par proc. proc_id+1;
									proc_to_left->recv(table_dist);	
									stop();

									//interclassertable et t dans m;
									for(int j=0; j<N/P;j++)
									{
										table_fusion[j]=table_locale.table[j];
										table_fusion[j+N/P]=table_dist.table[j];
									}
									//table = moitié basse de m;
									qsort (table_fusion, 2*N/P , sizeof(int), compare);
									for(int j=0; j<N/P;j++)
									{
										table_locale.table[j]=table_fusion[j];
									}
								}
/***********************/
										fprintf(Sdf::trace, "node %d , Fin itération %d  de la Phase 1 : \t", proc_id,k);
										fflush(Sdf::trace);

								for(int j=0; j<N/P; j++)
									{
										fprintf(Sdf::trace, "%d\t", table_locale.table[j]);
										fflush(Sdf::trace);
									}
										fprintf(Sdf::trace, "\n");
					 					fflush(Sdf::trace);
/*************************/

							//attendrela fin de réception;
							Sdf::barrier(cid1);

/*PHASE 2*/				if(proc_id > 0){
									c.more_SR(2);

									//envoyertable àproc. proc_id-1;
									proc_to_left->send(Sdf::STD,table_locale);
									//recevoirdans t la donnée table envoyée par proc. proc_id-1;
									proc_to_right->recv(table_dist);
									stop();

									//inter classer table et t dans m;
									for(int j=0; j<N/P;j++)
									{
										table_fusion[j]=table_locale.table[j];
										table_fusion[j+N/P]=table_dist.table[j];
									}
									//table = moitié haute de m;
									qsort (table_fusion, 2*N/P , sizeof(int), compare);
									for(int j=0; j<N/P;j++)
									{
										table_locale.table[j]=table_fusion[j+N/P];
									}
								}

/***********************/
										fprintf(Sdf::trace, "node %d , Fin itération %d  de la Phase 2 : \t", proc_id,k);
										fflush(Sdf::trace);

								for(int j=0; j<N/P; j++)
									{
										fprintf(Sdf::trace, "%d\t", table_locale.table[j]);
										fflush(Sdf::trace);
									}
										fprintf(Sdf::trace, "\n");
					 					fflush(Sdf::trace);
/*************************/

								//attendrela fin de réception;
								 Sdf::barrier(cid2);	
							}
/*processeur impair	*/
								else
								{
/*PHASE 1*/				c.more_SR(2);
									//recevoirdans t la donnée table envoyée par proc. proc_id-1;
									proc_to_right->recv(table_dist);
									//envoyertable àproc. proc_id-1;
									proc_to_left->send(Sdf::STD,table_locale);
									stop();

									//inter classer table et t dans m;
									for(int j=0; j<N/P;j++)
									{
										table_fusion[j]=table_locale.table[j];
										table_fusion[j+N/P]=table_dist.table[j];
									}
									//table = moitié haute de m;
									qsort (table_fusion, 2*N/P , sizeof(int), compare);
									for(int j=0; j<N/P;j++)
									{
										table_locale.table[j]=table_fusion[j+N/P];
									}

/***********************/
										fprintf(Sdf::trace, "node %d , Fin itération %d  de la Phase 1 : \t", proc_id,k);
										fflush(Sdf::trace);

								for(int j=0; j<N/P; j++)
									{
										fprintf(Sdf::trace, "%d\t", table_locale.table[j]);
										fflush(Sdf::trace);
									}
										fprintf(Sdf::trace, "\n");
					 					fflush(Sdf::trace);
/*************************/

									//attendrela fin de réception;
									 Sdf::barrier(cid1);
/*PHASE 2*/				if(proc_id < P -1){
									c.more_SR(2);
									//recevoirdans t la donnée table envoyée par proc. proc_id+1;
									proc_to_left->recv(table_dist);	
									//envoyer table àproc. proc_id+1;
									proc_to_right->send(Sdf::STD,table_locale);
									stop();

									//interclassertable et t dans m;
									for(int j=0; j<N/P;j++)
									{
										table_fusion[j]=table_locale.table[j];
										table_fusion[j+N/P]=table_dist.table[j];
									}
									//table = moitié basse de m;
									qsort (table_fusion, 2*N/P , sizeof(int), compare);
									for(int j=0; j<N/P;j++)
									{
										table_locale.table[j]=table_fusion[j];
									}
								}
/***********************/
										fprintf(Sdf::trace, "node %d , Fin itération %d  de la Phase 2 : \t", proc_id,k);
										fflush(Sdf::trace);

								for(int j=0; j<N/P; j++)
									{
										fprintf(Sdf::trace, "%d\t", table_locale.table[j]);
										fflush(Sdf::trace);
									}
										fprintf(Sdf::trace, "\n");
					 					fflush(Sdf::trace);
/*************************/
					//attendrela fin de réception;
					Sdf::barrier(cid2);
							}
					}
					

/***********************/
										fprintf(Sdf::trace, "node %d , TERMINE : \t", proc_id);
										fflush(Sdf::trace);

								for(int j=0; j<N/P; j++)
									{
										fprintf(Sdf::trace, "%d\t", table_locale.table[j]);
										fflush(Sdf::trace);
									}
										fprintf(Sdf::trace, "\n");
					 					fflush(Sdf::trace);
/*************************/


                delete proc_to_left, delete proc_to_right;

	#if TRACE_FILE == 2
		//These functions are library internal ones,
		//they are only allowed in library test mode
		//in this case, the trace is generated in the file ARCH_trace.
		Sched_intern::sched_data->sched_q_print(1);
		Sched_intern::sched_data->embryo_q_print();
		Sched_intern::sched_data->threadctrl_q_print();
	#endif
        }
};

//end of user program
MAIN(Root_proc, init_gid, Sdf::empty, Sdf::empty)
